package com.purchase.service.impl;

import com.purchase.entity.GoodsStock;
import com.purchase.entity.WxUser;
import com.purchase.mapper.GoodsStockMapper;
import com.purchase.mapper.OrderMapper;
import com.purchase.mapper.PayMapper;
import com.purchase.mapper.WxUserMapper;
import com.purchase.service.PayService;
import com.purchase.util.*;
import com.purchase.util.wxpay.SignValidUtil;
import com.purchase.util.wxpay.WXPayUtil;
import com.purchase.util.wxpay.WxpayHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
@Service
public class PayServiceImpl implements PayService {

    @Autowired
    private WxpayHandler wxpayHandler;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private PayMapper payMapper;

    @Autowired
    private GoodsStockMapper goodsStockMapper;

    @Autowired
    WxUserMapper wxUserMapper;

    @Override
    public ResponseForm doUnifiedOrder(RequestForm param) {
        ResponseForm result = new ResponseForm();
        Map<String, String> paramMap = (Map<String, String>) param.getData();
        try {
            String wxuid = paramMap.get("wxuid");
            String orderId = paramMap.get("orderId");
            if (!StringUtils.isNotBlank(wxuid)
                    || !StringUtils.isNotBlank(orderId)) {
                result.setStatus(false);
                result.setMessage(LogInfo.PARAM_ERROR);
                log.error(LogInfo.PARAM_ERROR);
                return result;
            }

            //校验库存
            List<Object> stockList = getStockList(orderId);
            if (stockList.size() != 0) {
                Map<String, Object> stockListMap = new HashMap<>();
                stockListMap.put("stockList", stockList);
                result.setData(stockListMap);
                return result;
            }

            WxUser wxUser = new WxUser();
            wxUser.setWxUid(wxuid);
            wxUser = wxUserMapper.selectOne(wxUser);
            String openId = wxUser.getOpenId();

            //组装支付信息
            Map<String, Object> order = orderMapper.getOrderByIdForPay(orderId);
            Map<String, Object> payOrderMap = new HashMap<>();
            payOrderMap.put("id", OrderIdGenerater.generatePayOrderId());
            payOrderMap.put("orderId", orderId);
            payOrderMap.put("totalFee", order.get("totalFee"));
            payOrderMap.put("goodsBody", "抢库存" + orderId);
            payOrderMap.put("openid", openId);

            //入库流水
            payMapper.savePayOrder(payOrderMap);

            //微信支付
            Map<String, String> map = wxpayHandler.doUnifiedOrder(payOrderMap);
            log.info(map.toString());

            result.setData(map);
        } catch (Exception e) {
            e.printStackTrace();
            result.setStatus(false);
            result.setMessage(LogInfo.ERROR);
            log.error(LogInfo.ERROR);
        }
        return result;
    }

    private List<Object> getStockList(String orderId) {
        //查该笔订单内详情的库存
        List<Map<String, Object>> detailList = orderMapper.getOrderDetailListByOrderId(orderId);
        //如果商品已下架加到 stockList，另如果库存小于订单内详情数量则将此详情放入一个无货 stockList 并给前端返回 stockList
        List<Object> stockList = new ArrayList<>();
        for (Map<String, Object> detail : detailList) {
            int soldout = (int) detail.get("soldout");
            if (soldout == 1) {
                stockList.add(detail);
            } else if ((int) detail.get("stock_num") < (int) detail.get("goods_num")) {
                stockList.add(detail);
            }
        }
        return stockList;
    }

    @Deprecated
    @Override
    public ResponseForm doGetSandboxSignKey(RequestForm param) {
        ResponseForm result = new ResponseForm();
        Map<String, String> paramMap = (Map<String, String>) param.getData();
        try {
            Map<String, Object> map = wxpayHandler.doGetSandboxSignKey();
            result.setData(map);
        } catch (Exception e) {
            e.printStackTrace();
            result.setStatus(false);
            result.setMessage(LogInfo.ERROR);
            log.error(LogInfo.ERROR);
        }
        return result;
    }

    @Override
    public String payNotify(HttpServletRequest request) {
        log.info("接收微信支付结果通知-开始");
        String resXml = "";
        Map resultMap = new HashMap();
        Map payOrderMap = new HashMap();
        Map updOrderMap = new HashMap();
        try {
            // 接收http请求处理
            String recXml = HttpClient.reciveHttpReq(request, "微信支付结果通知");
            log.debug("接收http request-xml-{}", recXml);

            // xml转map
            Map<String, String> recMap = WXPayUtil.xmlToMap(recXml);
            log.debug("xml to map {}", recMap);

            // 验签
            SignValidUtil.isSignatureValid(recMap);

            //解析微信xml
            String resultCode = recMap.get("result_code");
            String returnCode = recMap.get("return_code");

            //更新流水、订单
            if ("SUCCESS".equals(resultCode) && "SUCCESS".equals(returnCode)) {
                //通知支付成功
                String outTradeNo = recMap.get("out_trade_no");
                String transactionId = recMap.get("transaction_id");
                String endTime = recMap.get("time_end");
                String totalFee = recMap.get("total_fee");
                payOrderMap.put("payStatus", "S");
                payOrderMap.put("id", outTradeNo);
                payOrderMap.put("transactionId", transactionId);
                payOrderMap.put("endTime", endTime);
                Map payOrderById = payMapper.getPayOrderById(payOrderMap);
                //更新流水为成功
                payMapper.modifyPayOrder(payOrderMap);
                //更新订单为成功
                updOrderMap.put("orderStatus", "S");
                updOrderMap.put("logisticStatus", "LW");
                updOrderMap.put("orderId", payOrderById.get("order_id"));
                orderMapper.updateOrderStatusById(updOrderMap);
                //校验返回的订单金额是否与商户侧的订单金额一致，防止数据泄漏导致出现“假通知”，造成资金损失
                log.debug("微信返回金额：" + totalFee);
                log.debug("流水金额：" + String.valueOf(payOrderById.get("total_fee")));
                if (!totalFee.equals(yuan2fen(String.valueOf(payOrderById.get("total_fee"))))) {
                    log.error("流水金额：" + String.valueOf(payOrderById.get("total_fee")) + "与订单金额不一致");
                    resultMap.put("return_code", "FAIL");
                    resXml = WXPayUtil.mapToXml(resultMap);
                    //更新流水为失败
                    payOrderMap.put("payStatus", "F");
                    payMapper.modifyPayOrder(payOrderMap);
                    //更新订单为失败
                    updOrderMap.put("orderStatus", "F");
                    updOrderMap.put("logisticStatus", "LW");
                    updOrderMap.put("orderId", payOrderById.get("order_id"));
                    orderMapper.updateOrderStatusById(updOrderMap);
                    return resXml;
                }
            } else {
                //通知支付失败
                String outTradeNo = recMap.get("out_trade_no");
                String transactionId = recMap.get("transaction_id");
                String returnMsg = recMap.get("return_msg");
                String endTime = recMap.get("time_end");
                payOrderMap.put("payStatus", "F");
                payOrderMap.put("id", outTradeNo);
                payOrderMap.put("transactionId", transactionId);
                payOrderMap.put("endTime", endTime);
                payOrderMap.put("payFailDesc", returnMsg);
                Map payOrderById = payMapper.getPayOrderById(payOrderMap);
                String orderId = (String) payOrderById.get("order_id");
                payMapper.modifyPayOrder(payOrderMap);
                updOrderMap.put("orderStatus", "F");
                updOrderMap.put("logisticStatus", "LW");
                updOrderMap.put("orderId", orderId);
                orderMapper.updateOrderStatusById(updOrderMap);
                //还原库存（通过订单里的商品数量修改库存数量）
                restoreGoodsStock(orderId);
                log.debug("已还原库存");
            }

            // 给微信返回响应报文
            resultMap.put("return_msg", "OK");
            resultMap.put("return_code", "SUCCESS");

        } catch (Exception e) {
            log.error("接收微信支付结果通知异常", e);
            resultMap.put("return_code", "FAIL");
        }
        try {
            resXml = WXPayUtil.mapToXml(resultMap);
        } catch (Exception e) {
            log.error("接收微信支付结果通知", e);
        }
        log.info("接收微信支付结果通知-结束-{}", resXml);
        return resXml;
    }

    private void restoreGoodsStock(String orderId) {
        List<Map<String, Object>> detailList = orderMapper.getOrderDetailListByOrderId(orderId);
        for (Map<String, Object> detail : detailList) {
            int goodsId = (int) detail.get("goods_id");
            String specsName = (String) detail.get("specs_name");
            int goodsNum = (int) detail.get("goods_num");

            //查询库存
            GoodsStock goodsStock = new GoodsStock();
            goodsStock.setGoodsId(goodsId);
            goodsStock.setSpecsName(specsName);
            List<GoodsStock> goodsStocks = goodsStockMapper.selectByExample(goodsStock);
            Integer stockNum = goodsStocks.get(0).getStockNum();

            //更新商品库存
            goodsStock.setStockNum(stockNum + goodsNum);
            Example example = new Example(GoodsStock.class);
            Example.Criteria criteria = example.createCriteria();
            criteria.andEqualTo("goodsId", goodsId);
            criteria.andEqualTo("specsName", specsName);
            goodsStockMapper.updateByExampleSelective(goodsStock, example);
        }
    }

    private String getPayOrderId() {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSS");
        return df.format(new Date());
    }

    private String yuan2fen(String amount) {
        String currency = amount.replaceAll("[$￥,]", "");
        int index = currency.indexOf(".");
        int length = currency.length();
        Long amLong;
        if (index == -1) {
            amLong = Long.valueOf(currency + "00");
        } else if (length - index >= 3) {
            amLong = Long.valueOf((currency.substring(0, index + 3)).replace(".", ""));
        } else if (length - index == 2) {
            amLong = Long.valueOf((currency.substring(0, index + 2)).replace(".", "") + 0);
        } else {
            amLong = Long.valueOf((currency.substring(0, index + 1)).replace(".", "") + "00");
        }
        return amLong.toString();
    }

}
